package com.iwith.dongmoon.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;

import android.app.Activity;

public final class HttpRequestor {

	private class NullFile {
		NullFile() {
		}

		public String toString() {
			return "";
		}
	}

	public static final String CRLF = "\r\n";

	/**
	 * 연결할 URL
	 */
	private URL targetURL;

	/**
	 * 파라미터 목록을 저장하고 있다. 파라미터 이름과 값이 차례대로 저장된다.
	 */
	private ArrayList list;

	public HttpRequestor(URL target) {
		this(target, 20);
	}

	/**
	 * HttpRequest를 생성한다.
	 * 
	 * @param target
	 *            HTTP 메시지를 전송할 대상 URL
	 */
	public HttpRequestor(URL target, int initialCapicity) {
		this.targetURL = target;
		this.list = new ArrayList(initialCapicity);
	}

	/**
	 * 파라미터를 추가한다.
	 * 
	 * @param parameterName
	 *            파라미터 이름
	 * @param parameterValue
	 *            파라미터 값
	 * @exception IllegalArgumentException
	 *                parameterValue가 null일 경우
	 */
	public void addParameter(String parameterName, String parameterValue) {
		if (parameterValue == null)
			throw new IllegalArgumentException("parameterValue can't be null!");

		list.add(parameterName);
		list.add(parameterValue);
	}

	/**
	 * 파일 파라미터를 추가한다. 만약 parameterValue가 null이면(즉, 전송할 파일을 지정하지 않는다면 서버에 전송되는
	 * filename 은 "" 이 된다.
	 * 
	 * @param parameterName
	 *            파라미터 이름
	 * @param parameterValue
	 *            전송할 파일
	 * @exception IllegalArgumentException
	 *                parameterValue가 null일 경우
	 */
	public void addFile(String parameterName, File parameterValue) {

		if (parameterValue == null) {
			list.add(parameterName);
			list.add(new NullFile());
		} else {
			list.add(parameterName);
			list.add(parameterValue);
		}
	}

	private static String encodeString(ArrayList parameters) throws UnsupportedEncodingException {
		StringBuffer sb = new StringBuffer(256);

		Object[] obj = new Object[parameters.size()];
		parameters.toArray(obj);

		for (int i = 0; i < obj.length; i += 2) {
			if (obj[i + 1] instanceof File || obj[i + 1] instanceof NullFile)
				continue;

			sb.append(URLEncoder.encode((String) obj[i], "UTF-8"));
			sb.append('=');
			sb.append(URLEncoder.encode((String) obj[i + 1], "UTF-8"));

			if (i + 2 < obj.length)
				sb.append('&');
		}

		return sb.toString();
	}

	/**
	 * GET 방식으로 대상 URL에 파라미터를 전송한 후 응답을 InputStream으로 리턴한다.
	 * 
	 * @return InputStream
	 */
	public InputStream sendGet() throws IOException {
		String paramString = null;
		if (list.size() > 0)
			paramString = "?" + encodeString(list);
		else
			paramString = "";

		URL url = new URL(targetURL.toExternalForm() + paramString);

		URLConnection conn = url.openConnection();

		return conn.getInputStream();
	}

	/**
	 * POST 방식으로 대상 URL에 파라미터를 전송한 후 응답을 InputStream으로 리턴한다.
	 * 
	 * @return InputStream
	 */
	public InputStream sendPost(Activity activity) throws IOException {
		String paramString = null;
		if (list.size() > 0)
			paramString = encodeString(list);
		else
			paramString = "";

		// HttpURLConnection conn = (HttpURLConnection)
		// targetURL.openConnection();
		HttpURLConnection conn = ConnectionManager.getConnection(activity, targetURL);
		conn.setRequestMethod("POST");
		conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
		conn.setDoInput(true);
		conn.setDoOutput(true);
		conn.setUseCaches(false);

		DataOutputStream out = null;
		try {
			out = new DataOutputStream(conn.getOutputStream());
			out.writeBytes(paramString);
			out.flush();
		} finally {
			if (out != null)
				out.close();
		}

		conn.connect();
		
		ConnectionManager.setCookie(activity, conn);
		
		return conn.getInputStream();
	}

	public InputStream sendMultipartPost(Activity activity) throws IOException {
		// HttpURLConnection conn =
		// (HttpURLConnection)targetURL.openConnection();
		HttpURLConnection conn = ConnectionManager.getConnection(activity, targetURL);

		// Delimeter 생성
		String delimeter = makeDelimeter();
		byte[] newLineBytes = CRLF.getBytes();
		byte[] delimeterBytes = delimeter.getBytes();
		byte[] dispositionBytes = "Content-Disposition: form-data; name=".getBytes();
		byte[] quotationBytes = "\"".getBytes();
		byte[] contentTypeBytes = "Content-Type: application/octet-stream".getBytes();
		byte[] fileNameBytes = "; filename=".getBytes();
		byte[] twoDashBytes = "--".getBytes();

		conn.setRequestMethod("POST");
		conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + delimeter);
		conn.setDoInput(true);
		conn.setDoOutput(true);
		conn.setUseCaches(false);

		BufferedOutputStream out = null;
		try {
			out = new BufferedOutputStream(conn.getOutputStream());

			Object[] obj = new Object[list.size()];
			list.toArray(obj);

			for (int i = 0; i < obj.length; i += 2) {
				// Delimeter 전송
				out.write(twoDashBytes);
				out.write(delimeterBytes);
				out.write(newLineBytes);
				// 파라미터 이름 출력
				out.write(dispositionBytes);
				out.write(quotationBytes);
				out.write(((String) obj[i]).getBytes());
				out.write(quotationBytes);
				if (obj[i + 1] instanceof String) {
					// String 이라면
					out.write(newLineBytes);
					out.write(newLineBytes);
					// 값 출력
					//out.write(URLEncoder.encode(((String) obj[i + 1]), "UTF-8").getBytes());
					out.write(((String)obj[i + 1]).getBytes());
					out.write(newLineBytes);
				} else {
					// 파라미터의 값이 File 이나 NullFile인 경우
					if (obj[i + 1] instanceof File) {
						File file = (File) obj[i + 1];
						// File이 존재하는 지 검사한다.
						out.write(fileNameBytes);
						out.write(quotationBytes);
						out.write(file.getAbsolutePath().getBytes());
						out.write(quotationBytes);
					} else {
						// NullFile 인 경우
						out.write(fileNameBytes);
						out.write(quotationBytes);
						out.write(quotationBytes);
					}
					out.write(newLineBytes);
					out.write(contentTypeBytes);
					out.write(newLineBytes);
					out.write(newLineBytes);
					// File 데이터를 전송한다.
					if (obj[i + 1] instanceof File) {
						File file = (File) obj[i + 1];
						// file에 있는 내용을 전송한다.
						BufferedInputStream is = null;
						try {
							is = new BufferedInputStream(new FileInputStream(file));
							byte[] fileBuffer = new byte[1024 * 8]; // 8k
							int len = -1;
							while ((len = is.read(fileBuffer)) != -1) {
								out.write(fileBuffer, 0, len);
							}
						} finally {
							if (is != null)
								try {
									is.close();
								} catch (IOException ex) {
								}
						}
					}
					out.write(newLineBytes);
				} // 파일 데이터의 전송 블럭 끝
				if (i + 2 == obj.length) {
					// 마지막 Delimeter 전송
					out.write(twoDashBytes);
					out.write(delimeterBytes);
					out.write(twoDashBytes);
					out.write(newLineBytes);
				}
			} // for 루프의 끝

			out.flush();
		} finally {
			if (out != null)
				out.close();
		}

		conn.connect();
		ConnectionManager.setCookie(activity, conn);

		return conn.getInputStream();
	}

	private static String makeDelimeter() {
		return "---------------------------7d115d2a20060c";
	}
}